#-*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

__all__ = ['Parameter', 'ParameterSet']

"""
Advanced containers based on Container and Datum
"""

from pyLot.minimal import (Datum, Container, check_bool, CustomException)
from pyLot.application import APPLICATION

class ErrReadOnly(CustomException):
  title = u'Read only value cannot be changed'
  message = u'%(param_class)s %(name)s is read only.'
  desc = "%(param_class)s %(name)s is read only. Value cannot be changed from %(old)s to %(new)s"

  def _kargs(self):
    p = self._args[0]
    return dict(
      param_class=unicode(p.__class__.__name__),
      name=p.name,
      old=p.value,
      new=self._args[1],
      )

class Parameter(Datum):
  u"""
  Extension of Datum adding two fields :
    - enabled=True/False
    - read_only=True/False
  If read_only is True, changing value raises an exception.

  >>> p = Parameter(u'p', int, 1, read_only=True)
  >>> p.value = 2
  Traceback (most recent call last):
  ...
  ErrReadOnly: Parameter p1 is read only. Value cannot be changed from 1 to 2
  """
  ordered_keys = tuple(list(Datum.ordered_keys) + [u'enabled', u'read_only'])

  def __init__(self, name=None, nature=None, value=None, default_value=None,
               value_list=None, tooltip=None, group=None, alias=None, enabled=True,
               read_only=False):

    self.setValue = self._setValue
    Datum.__init__(self, name=name, nature=nature, value=value,
                   default_value=default_value, value_list=value_list,
                   tooltip=tooltip, group=group, alias=alias,
                   enabled=enabled, read_only=read_only)

    self._value_aliases = {}
    for field in (u'alias', u'tooltip') :
      self._value_aliases[field] = {}

  def _setValue(self, value):
    Datum._setvalue(self, value)

  def _setReadOnlyValue(self, value):
    raise ErrReadOnly(self, value)

  def _setvalue(self, value):
    self.setValue(value)

  def _getValidNature(self):
    pm = APPLICATION.getPluginManager()
    return tuple(list(Datum.valid_nature) + pm.get_extensions('pyLot.core.natures'))

  value = property(fget=lambda self:self._value, fset=_setvalue)
  valid_nature = property(fget=_getValidNature)

  def setReadOnly(self, value):
    value = check_bool(u'read_only', value, none=True)
    if value is None :
      self._read_only = False
    else :
      self._read_only = value
    if self._read_only is True :
      self.setValue = self._setReadOnlyValue
    else :
      self.setValue = self._setValue

  def setEnabled(self, value):
    value = check_bool(u'enabled', value, none=True)
    if value is None :
      self._enabled = True
    else :
      self._enabled = value

  read_only = property(fget=lambda self:self._read_only,
                       fset=setReadOnly)

  enabled = property(fget=lambda self:self._enabled,
                     fset=setEnabled)

  def setValueField(self, value, alias, field=u'alias'):
    nature, value = self.arrangeValue(value, self._nature)
    self._value_aliases[field][value] = alias

  def getValueField(self, value, field=u'alias'):
    nature, value = self.arrangeValue(value, self._nature)
    try :
      field = self._value_aliases[field][value]
    except TypeError:
      raise KeyError, value
    else :
      return field

  def setValueAlias(self, value, alias):
    return self.setValueField(value, alias)

  def getValueAlias(self, value):
    return self.getValueField(value)

  def valueToXmlElement (self, value, role=None):
    node = super(Parameter, self).valueToXmlElement(value, role)
    try :
      alias = self.getValueAlias(value)
    except KeyError:
      pass
    else :
      node.attrib[u'alias'] = alias
    return node

class ParameterSet(Container):

  ordered_keys = tuple(list(Container.ordered_keys) + [u'enabled', u'read_only'])

  def __init__(self, name=None, nature=None, tooltip=None, group=None, alias=None,
    data=[], datum_type=Parameter, enabled=True, read_only=False):
    Container.__init__(self, name=name, nature=nature, tooltip=tooltip,
                       group=group, alias=alias, data=data, datum_type=datum_type)

    self._signal_methods[u'enabledChanged'] = set([])

    self._read_only = None
    self._enabled = None

    self.read_only = read_only
    self.enabled = enabled

  def setReadOnly(self, value):
    value = check_bool(u'read_only', value, none=True)
    self._read_only = value
    for datum in self.datumValues():
      datum.read_only = value

  def setEnabled(self, value):
    old_value = self._enabled
    value = check_bool(u'enabled', value, none=True)
    self._enabled = value
    for datum in self.datumValues():
      datum.enabled = value

    if old_value != self._enabled:
      self._emitSignal('enabledChanged', self, old_value, self._enabled)

  read_only = property(fget=lambda self:self._read_only,
                       fset=setReadOnly)

  enabled = property(fget=lambda self:self._enabled,
                     fset=setEnabled)
